home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
HAM Radio 3.2
/
Ham Radio Version 3.2 (Chestnut CD-ROMs)(1993).ISO
/
packet
/
n17jsrc
/
hs.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-02-11
|
19KB
|
749 lines
/* Interface driver for the DRSI PCPA or the Eagle 8530 boards for the IBM PC
* connected to a WA4DSY 56kbps modem. Uses polling-loop transfers with
* interrupts disabled for maximum speed.
*
* This driver is a bit of a kludge. A DMA-driven card and driver (e.g.,
* the PI) is much better, but this is better than nothing if all you have
* is a "dumb" 8530 card.
*
* Copyright 1991 Phil Karn, KA9Q
*/
#include <stdio.h>
#include <dos.h>
#include "global.h"
#include "mbuf.h"
#include "iface.h"
#include "pktdrvr.h"
#include "netuser.h"
#include "hs.h"
#include "8530.h"
#include "ax25.h"
#include "trace.h"
#include "pc.h"
#include "proc.h"
#include "devparam.h"
static void flushrx __ARGS((int16 data));
static void hdlcparam __ARGS((struct hdlc *hp));
static void hexint __ARGS((struct hdlc *hp));
static void hrxint __ARGS((struct hdlc *hp));
static int hs_stop __ARGS((struct iface *iface));
static int hs_raw __ARGS((struct iface *iface,struct mbuf *bp));
static void hs_tx __ARGS((int unused,void *hp1,void *a));
static int32 hs_ctl __ARGS((struct iface *,int cmd,int set,int32 val));
static void hstxoff __ARGS((struct hdlc *hp));
static void hstxon __ARGS((struct hdlc *hp));
static void htxint __ARGS((struct hdlc *hp));
static void init_delay __ARGS((void));
static void msdelay __ARGS((void));
static struct hs Hs[NHS];
static INTERRUPT (*Hshandle[])() = { hs0vec };
static struct hdlc Hdlc[2*NHS];
static int16 Nhs;
/* Master interrupt handler for the PC-100 card. All interrupts come
* here first, then are switched out to the appropriate routine.
*/
void
hsint(dev)
int dev;
{
register char iv;
int16 hsbase;
register struct hdlc *hp;
Hs[dev].ints++;
hsbase = Hs[dev].addr;
#ifdef foo
outportb(hsbase+4,0x8+0x10); /* HIT EAGLE INTACK */
(void)inportb(hsbase+CHANA+CTL,R0);
outportb(hsbase+4,0x8); /***/
#endif
/* Read interrupt status from channel A */
while((iv = read_scc(hsbase+CHANA+CTL,R3)) != 0){
if(iv & CHARxIP){
/* Channel A Rcv Interrupt Pending */
hp = &Hdlc[2*dev];
hrxint(hp);
} else if(iv & CHATxIP){
/* Channel A Transmit Int Pending */
hp = &Hdlc[2*dev];
htxint(hp);
} else if(iv & CHAEXT){
/* Channel A External Status Int */
hp = &Hdlc[2*dev];
hexint(hp);
} else if(iv & CHBRxIP){
/* Channel B Rcv Interrupt Pending */
hp = &Hdlc[(2*dev)+1];
hrxint(hp);
} else if(iv & CHBTxIP){
/* Channel B Transmit Int Pending */
hp = &Hdlc[(2*dev)+1];
htxint(hp);
} else if(iv & CHBEXT){
/* Channel B External Status Int */
hp = &Hdlc[(2*dev)+1];
hexint(hp);
}
/* Reset interrupt pending state */
write_scc(hp->ctl,R0,RES_H_IUS);
outportb(hsbase+CHANA+CTL,0); /* Restore pointer to 0 */
outportb(hsbase+CHANB+CTL,0); /* Restore pointer to 0 */
}
outportb(hsbase+CHANA+CTL,0); /* Restore pointer to 0 */
outportb(hsbase+CHANB+CTL,0); /* Restore pointer to 0 */
}
/* HDLC SIO External/Status interrupts
* The only one that can happen in this driver is a DCD change
*/
static void
hexint(hp)
register struct hdlc *hp;
{
struct mbuf *rcvbuf;
struct phdr phdr;
char *cp;
int cnt,data;
register int ctl;
ctl = hp->ctl;
data = hp->data;
hp->exints++;
/* Allocate a receive buffer */
if((rcvbuf = alloc_mbuf(hp->bufsiz+sizeof(phdr))) == NULLBUF){
/* Alloc failed; refuse to proceed */
hp->nomem++;
write_scc(ctl,R3,ENT_HM|RxENABLE|RxCRC_ENAB|Rx8);
write_scc(ctl,R0,RES_EXT_INT);
return;
}
/* Allow space for phdr descriptor on front */
cp = rcvbuf->data + sizeof(phdr);
cnt = 0;
/* Disable DCDIE bit so we can track changes in DCD */
write_scc(ctl,R15,0);
write_scc(ctl,R3,ENT_HM|RxENABLE|RxCRC_ENAB|Rx8);
flushrx(data);
while((cnt = rx8530(ctl,data,cp,hp->bufsiz)) != -1){
if(cnt > 4){
/* Good frame */
hp->good++;
/* Toss crc */
rcvbuf->cnt = sizeof(phdr) + cnt - 1;
phdr.iface = hp->iface;
phdr.type = CL_AX25;
memcpy(rcvbuf->data,(char *)&phdr,sizeof(phdr));
enqueue(&Hopper,rcvbuf);
/* Replenish buffer */
rcvbuf = alloc_mbuf(hp->bufsiz + sizeof(phdr));
}
/* Start new buffer */
if(rcvbuf == NULLBUF)
break; /* alloc failed */
cp = rcvbuf->data + sizeof(phdr);
}
write_scc(ctl,R0,RES_EXT_INT);
write_scc(ctl,R15,DCDIE); /* Re-enable DCD */
write_scc(ctl,R3,ENT_HM|RxENABLE|RxCRC_ENAB|Rx8);
/* Get rid of fragmentary buffer */
free_p(rcvbuf);
}
static void
flushrx(data)
register int16 data;
{
register int i = 5;
while(i-- != 0)
(void)inportb(data);
}
/* HDLC receiver interrupt handler.
* Not used in this driver
*/
static void
hrxint(hp)
register struct hdlc *hp;
{
}
/* HDLC transmit interrupt service routine
* Not used in this driver
*/
static void
htxint(hp)
register struct hdlc *hp;
{
}
/* (re)Initialize HDLC controller parameters */
static void
hdlcparam(hp)
register struct hdlc *hp;
{
char i_state;
register int16 ctl;
/* Initialize 8530 channel for SDLC operation */
ctl = hp->ctl;
i_state = dirps();
#ifdef foo
switch(ctl & 2){
case CHANA:
write_scc(ctl,R9,CHRA); /* Reset channel A */
break;
case CHANB:
write_scc(ctl,R9,CHRB); /* Reset channel B */
break;
}
pause(1L); /* Allow plenty of time for resetting */
#endif
/* Deselect interrupts for now */
write_scc(ctl,R1,0);
write_scc(ctl,R15,0);
/* X1 clock, SDLC mode, Sync modes enable, parity disable */
write_scc(ctl,R4,X1CLK | SDLC | SYNC_ENAB);
/* CRC preset 1, NRZ encoding, no active on poll, flag idle,
* flag on underrun, no loop mode, 8 bit sync
*/
write_scc(ctl,R10,CRCPS|NRZ);
/* 8530 gets both tx and rx clock from modem.
* By default, TRxC = transmit clock, RTxC = receive clock
* (swapped 11 Feb 1990 to use new DRSI wiring) UNLESS
* the 'r' parameter is specified
*/
if(!hp->clkrev)
write_scc(ctl,R11,RCRTxCP | TCTRxCP);
else
write_scc(ctl,R11,RCTRxCP | TCRTxCP);
/* Note: baud rate generator not used */
/* Null out SDLC start address */
write_scc(ctl,R6,0);
/* SDLC flag */
write_scc(ctl,R7,FLAG);
/* DTR On, 8 bit TX chars, no break, TX enable, SDLC CRC,
* RTS off, TxCRC enable
*/
write_scc(ctl,R5,DTR|Tx8|TxENAB|TxCRC_ENAB);
/* 8 bit RX chars, auto enables off, no hunt mode, RxCRC enable,
* no address search, no inhibit sync chars, disable RX. Rx is
* started only by an actual DCD interrupt
*/
write_scc(ctl,R3,RxENABLE|RxCRC_ENAB|Rx8);
/* Dummy interrupt vector
* (This probably isn't necessary)
*/
write_scc(ctl,R2,0);
/* Enable only the external interrupts (modem interrupts) since
* polling is used for all actual tx/rx operations
*/
write_scc(ctl,R1,EXT_INT_ENAB);
/* Enable only DCD interrupts */
write_scc(ctl,R15,DCDIE);
/* No reset, status low, master int enable, enable lower chain,
* no vector
*/
write_scc(ctl,R9,MIE|NV);
restore(i_state);
}
/* Attach a high speed iterface to the system
* argv[0]: hardware type, must be "hs"
* argv[1]: I/O address, e.g., "0x380"
* argv[2]: vector, e.g., "2"
* argv[3]: mode, must be "ax25"
* argv[4]: interface base label, e.g., "drsi0". Driver appends "a" and "b".
* argv[5]: receiver packet buffer size in bytes
* argv[6]: maximum transmission unit, bytes
* argv[7]: keyup delay, milliseconds
* argv[8]: persistence value, 0-255
* argv[9]: "r" to reverse sense of clock leads (optional)
*/
int
hs_attach(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register struct iface *if_hsa,*if_hsb;
struct hdlc *hp;
int dev;
if(Nhs >= NHS){
tprintf("Too many hs controllers\n");
return -1;
}
if(if_lookup(argv[4]) != NULLIF){
tprintf("Interface %s already exists\n",argv[4]);
return -1;
}
dev = Nhs++;
/* Initialize hardware-level control structure */
Hs[dev].addr = htoi(argv[1]);
Hs[dev].vec = htoi(argv[2]);
/* Save original interrupt vector */
Hs[dev].save.vec = getirq(Hs[dev].vec);
/* Set new interrupt vector */
if(setirq(Hs[dev].vec,Hshandle[dev]) == -1){
tprintf("IRQ %u out of range\n",Hs[dev].vec);
Nhs--;
return -1;
}
/* Create interface structures and fill in details */
if_hsa = (struct iface *)callocw(1,sizeof(struct iface));
if_hsb = (struct iface *)callocw(1,sizeof(struct iface));
if_hsa->addr = if_hsb->addr = Ip_addr;
if_hsa->name = mallocw(strlen(argv[4])+2);
strcpy(if_hsa->name,argv[4]);
strcat(if_hsa->name,"a");
if_hsb->name = mallocw(strlen(argv[4])+2);
strcpy(if_hsb->name,argv[4]);
strcat(if_hsb->name,"b");
if_hsb->mtu = if_hsa->mtu = atoi(argv[6]);
if_hsb->type = if_hsa->type = CL_AX25;
if_hsa->dev = 2*dev;
if_hsb->dev = 2*dev + 1;
if_hsb->stop = if_hsa->stop = hs_stop;
if_hsb->output = if_hsa->output = ax_output;
if_hsb->raw = if_hsa->raw = hs_raw;
if_hsa->ioctl = if_hsb->ioctl = hs_ctl;
if(strcmp(argv[3],"ax25") == 0){
if(Mycall[0] == '\0'){
tprintf("set mycall first\n");
free((char *)if_hsa);
free((char *)if_hsb);
return -1;
}
if_hsb->send = if_hsa->send = ax_send;
if(if_hsb->hwaddr == NULLCHAR)
if_hsb->hwaddr = mallocw(AXALEN);
memcpy(if_hsb->hwaddr,Mycall,AXALEN);
if(if_hsa->hwaddr == NULLCHAR)
if_hsa->hwaddr = mallocw(AXALEN);
memcpy(if_hsa->hwaddr,Mycall,AXALEN);
} else {
tprintf("Mode %s unknown for interface %s\n",
argv[3],argv[4]);
free((char *)if_hsa);
free((char *)if_hsb);
return -1;
}
if_hsa->next = if_hsb;
if_hsb->next = Ifaces;
Ifaces = if_hsa;
write_scc(Hs[dev].addr+CHANA+CTL,R9,FHWRES);
hp = &Hdlc[2*dev+1];
hp->ctl = Hs[dev].addr + CHANB + CTL;
hp->data = Hs[dev].addr + CHANB + DATA;
hp->bufsiz = atoi(argv[5]);
if(argc > 7)
hp->txdelay = atol(argv[7]);
else
hp->txdelay = 15L;
if(argc > 8)
hp->p = atoi(argv[8]);
else
hp->p = 64;
if(argc > 9 && argv[9][0] == 'r')
hp->clkrev = 1;
else
hp->clkrev = 0;
hp->iface = if_hsb;
hdlcparam(hp);
if_hsa->txproc = newproc("hs_tx",1024,hs_tx,0,hp,NULL,0);
hp = &Hdlc[2*dev];
hp->ctl = Hs[dev].addr + CHANA + CTL;
hp->data = Hs[dev].addr + CHANA + DATA;
hp->bufsiz = atoi(argv[5]);
hp->txdelay = Hdlc[2*dev+1].txdelay;
hp->p = Hdlc[2*dev+1].p;
if(argc > 9 && argv[9][0] == 'r')
hp->clkrev = 1;
else
hp->clkrev = 0;
hp->iface = if_hsa;
hdlcparam(hp);
if_hsb->txproc = newproc("hs_tx",1024,hs_tx,0,hp,NULL,0);
outportb(Hs[dev].addr + 4,0x08); /*EAGLE INT GATE */
/* Clear mask (enable interrupt) in 8259 interrupt controller */
maskon(Hs[dev].vec);
/* Initialize timing delay loop */
init_delay();
return 0;
}
static int
hs_stop(iface)
struct iface *iface;
{
int dev;
dev = iface->dev;
if(dev & 1)
return -1; /* Valid only for the first device */
dev >>= 1; /* Convert back into hs number */
/* Turn off interrupts */
maskoff(Hs[dev].vec);
/* Restore original interrupt vector */
setirq(Hs[dev].vec,Hs[dev].save.vec);
/* Force hardware reset */
write_scc(Hs[dev].addr + CHANA+CTL,R9,FHWRES);
return 0;
}
/* Send raw packet */
static int
hs_raw(iface,bp)
struct iface *iface;
struct mbuf *bp;
{
struct hdlc *hp;
dump(iface,IF_TRACE_OUT,CL_AX25,bp);
iface->rawsndcnt++;
iface->lastsent = secclock();
hp = &Hdlc[iface->dev];
hp->txpkts++;
enqueue(&hp->txq,bp); /* Put on queue for hs_tx process */
return 0;
}
/* High speed transmit process */
void
hs_tx(unused,hp1,a)
int unused;
void *hp1;
void *a; /* Unused */
{
struct mbuf *bp,*nbp;
register int16 cnt;
register char *cp;
int16 ctl,data;
int txon = 0; /* Transmitter on/off state */
struct hdlc *hp;
int i;
hp = (struct hdlc *)hp1;
ctl = hp->ctl;
data = hp->data;
for(;;){
/* Wait for work */
while(hp->txq == NULLBUF){
if(txon){
/* No more frames, shut down tx */
hstxoff(hp);
txon = 0;
/* Hold off to give other guy a chance to
* respond
*/
hp->deftime = msclock() + hp->txdelay + 500;
}
pwait(&hp->txq);
}
bp = dequeue(&hp->txq);
cnt = len_p(bp);
/* If buffer isn't contiguous (which is almost always
* the case) copy it to a new buffer for speed
*/
if(bp->next != NULLBUF){
if((nbp = copy_p(bp,cnt)) == NULLBUF){
hp->nomem++;
free_p(bp);
continue;
}
free_p(bp);
bp = nbp;
}
cp = bp->data;
/* Turn transmitter on if necessary */
if(!txon){
hstxon(hp);
txon = 1;
} else {
/* Else wait another txd for receiver to get ready again */
for(i=hp->txdelay;i != 0;i--)
msdelay();
}
/* Initialize transmitter CRC */
write_scc(ctl,R0,RES_Tx_CRC);
for(;;){
/* Wait for the transmitter to become ready */
while(!(inportb(ctl) & Tx_BUF_EMP))
;
if(cnt-- == 0)
break;
outportb(data,*cp++); /* Send the character */
}
write_scc(ctl,R0,RES_EOM_L); /* Allow CRC generation */
/* End of frame. Wait for TxEOM to go high, indicating start of
* CRC transmission. Note that we don't reset the transmit
* interrupt pending flag as one ordinarily would, since we're
* not using tx interrupts.
*/
while(!(inportb(ctl) & TxEOM))
;
free_p(bp);
}
}
/* Turn on high speed transmitter. Does p-persistence, then sends a dummy
* frame to allow for keyup delay. Returns with transmitter on and interrupts
* disabled
*/
static void
hstxon(hp)
struct hdlc *hp;
{
int16 ctl;
int i;
long ca;
int32 t;
ctl = hp->ctl;
/* Defer logic. Wait until deftime is in the past (so we
* defer to any overheard CTS messages) AND the p-persistence
* dice roll succeeds. The computation of ca allows for clock
* rollover (which happens every 49+ days).
*/
for(;;){
t = msclock();
ca = hp->deftime - t;
if(ca > 0){
pause(ca);
continue;
}
hp->deftime = t; /* Keep from getting too old */
if((rand() & 0xff) > uchar(hp->p)){
pause((long)MSPTICK);
continue;
}
break;
}
/* Prevent distractions. In particular, block off the DCD interrupt
* so we don't hear our own carrier and hang in the interrupt handler!
* Note that simply disabling CPU interrupts isn't enough since
* the call to pause will block and the kernel will re-enable
* them.
*/
write_scc(ctl,R9,0); /* Disable all SCC interrupts */
(void)dirps(); /* Return value is always 1 */
/* Turn on carrier, enable transmitter */
write_scc(ctl,R5,TxCRC_ENAB | RTS | TxENAB | Tx8 | DTR);
/* Delay for keyup interval */
for(i=hp->txdelay;i != 0;i--)
msdelay();
}
/* Turn transmitter off at the end of a series of frames */
static void
hstxoff(hp)
struct hdlc *hp;
{
int cnt;
int16 ctl,data;
ctl = hp->ctl;
data = hp->data;
/* To allow the SCC buffering to drain, we begin a dummy frame,
* then abort it
*/
for(cnt=5;cnt != 0;cnt--){
while(!(inportb(ctl) & Tx_BUF_EMP))
;
outportb(data,0);
}
write_scc(ctl,R0,SEND_ABORT);
/* Turn off carrier and disable transmitter */
write_scc(ctl,R5,TxCRC_ENAB | Tx8 | DTR);
/* Re-Enable SCC interrupts */
write_scc(ctl,R9,MIE|NV);
restore(1); /* Turn interrupts back on */
}
int
dohs(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register int i;
register struct hdlc *hp;
for(i=0;i<2*Nhs;i++){
hp = &Hdlc[i];
if(tprintf("port %d: txpkts %lu ints %lu rxpkts %lu rxbytes %lu nomem %lu toobig %lu crcerr %lu aborts %lu overrun %lu\n",
i,hp->txpkts,hp->exints,hp->good,hp->rxbytes,
hp->nomem,hp->toobig,hp->crcerr,hp->aborts,
hp->overrun) == EOF)
break;
}
return 0;
}
static int32
hs_ctl(iface,cmd,set,val)
struct iface *iface;
int cmd;
int set;
int32 val;
{
register struct hdlc *hp;
int32 t,ca;
hp = &Hdlc[iface->dev];
switch(cmd){
case PARAM_TXDELAY: /* Tx keyup delay */
if(set)
hp->txdelay = val;
return hp->txdelay;
case PARAM_PERSIST:
if(set)
hp->p = val;
return uchar(hp->p);
case PARAM_MUTE:
/* Mute transmitter for specified # of ms */
if(set){
if(val == -1){
/* Special case for duration of a CTS */
val = hp->txdelay + 500;
}
hp->deftime = msclock() + val;
}
t = msclock();
ca = hp->deftime - t;
if(ca < 0){
hp->deftime = t;
ca = 0;
}
return ca;
}
return -1;
}
#ifdef notdef /* replaced with assembler in 8530.asm */
/* Read data from the 8530 receiver.
* Returns when either a good frame is received, or when carrier drops.
* If a good frame is received, the length is returned; otherwise -1.
*/
int
rx8530(ctl,data,buf,bufsize)
int16 ctl,data;
char *buf;
int16 bufsize;
{
int cnt = 0;
register char status;
char error;
register char *cp = buf;
for(;;){
status = inportb(ctl);
if(!(status & DCD)){
cnt = -1;
break;
} else if(status & BRK_ABRT){
cp = buf;
cnt = 0;
} else if(status & Rx_CH_AV){
/* Receive character is ready, get it */
*cp++ = inportb(data);
if(++cnt > bufsize){
/* Buffer overflow, start again */
write_scc(ctl,R3,ENT_HM|RxENABLE|RxCRC_ENAB|Rx8);
cp = buf;
cnt = 0;
}
} else if((error = read_scc(ctl,R1)) & END_FR){
if(!(error & CRC_ERR))
break; /* Good frame! */
/* Bad frame, start again */
cp = buf;
cnt = 0;
}
}
return cnt;
}
#endif
static int32 Del_const;
/* Find the value of Del_const that will cause one execution of mloop()
* to take one millisecond
*/
static void
init_delay()
{
int32 start,delay;
register int i,j;
int success = 0;
/* Start with small value to make things tolerable on slow machines */
Del_const = 10;
printf("Del_const = %lu\n",Del_const);
/* Limit the number of iterations in case we don't converge */
for(i=0;i<5;i++){
start = msclock();
for(j=0;j<1000;j++)
msdelay();
delay = msclock()-start;
printf("delay %lu\n",delay);
if(delay == 0){
/* Too fast for accurate measurement on coarse clk */
Del_const *= 10;
printf("Del_const = %lu\n",Del_const);
continue;
}
Del_const = (Del_const * 1000)/delay;
printf("Del_const = %lu\n",Del_const);
if(delay > 950 && delay < 1050){
success = 1;
break; /* Within 1 tick - Close enough */
}
}
if(!success)
tprintf("HS: Warning: auto delay set failed\n");
}
/* Delay for one millisecond (once calibrated by init_delay()) */
static void
msdelay()
{
int32 i;
for(i=Del_const;i !=0;i--)
;
}